VERSIONS = ["2.1.2", "2.1.3", "2.1.7", "2.1.10", "2.2.0", "3.2.3", "3.2.4"]
HISTORIC_VERSIONS = ["2.1.2", "2.1.3", "2.1.7", "2.1.10"]
#DEFAULT_VERSION = "2.2.0"
DEFAULT_VERSION = "3.2.4"
DEPENDS = ['openpgm']

def processurl(machine, dirs, version):
  return ("http://download.zeromq.org/%(HISTORIC)szeromq-%(VERSION)s.zip", "http://download.zeromq.org/%(HISTORIC)szeromq-%(VERSION)s.tar.gz")[machine.isLinux()] % {"VERSION":version, 
                                                                                                                                                                     "HISTORIC":"historic/" if version in HISTORIC_VERSIONS else ""}

URL = processurl
URLCPPZMQ = "https://github.com/zeromq/cppzmq.git/trunk"
#URLCPPZMQZIP = "https://github.com/zeromq/cppzmq/archive/master.zip"

from source.builders import VisualStudioBuilder, ConfigureMakeBuilder, FetchSourceSVN
#from source.machine import DownloadExtract
import os, subprocess, shutil, ext.patch, string, sys

#note, possible error:
#cannot link with -luuid, install uuid-dev to build the package.

# Define patch strings
patchstr_vcproj_zmq2 = """
--- builds\msvc\libzmq\libzmq.vcproj
+++ builds\msvc\libzmq\libzmq.vcproj
@@ -42,6 +42,7 @@
 \t\t\t\tName="VCCLCompilerTool"
 \t\t\t\tAdditionalOptions="-DDLL_EXPORT -DFD_SETSIZE=1024"
 \t\t\t\tOptimization="0"
+\t\t\t\tAdditionalIncludeDirectories="$_pgmdir_/include"
 \t\t\t\tMinimalRebuild="true"
 \t\t\t\tBasicRuntimeChecks="3"
 \t\t\t\tRuntimeLibrary="3"
@@ -59,8 +60,8 @@
 \t\t\t/>
 \t\t\t<Tool
 \t\t\t\tName="VCLinkerTool"
-\t\t\t\tAdditionalDependencies="Ws2_32.lib Rpcrt4.lib"
-\t\t\t\tOutputFile="../../../lib/libzmq.dll"
+\t\t\t\tAdditionalDependencies="Ws2_32.lib Rpcrt4.lib $_pgmdir_/lib/libpgm.lib"
+\t\t\t\tOutputFile="../../../lib/libzmqd.dll"
 \t\t\t\tGenerateDebugInformation="true"
 \t\t\t\tTargetMachine="1"
 \t\t\t/>
@@ -115,6 +116,7 @@
 \t\t\t\tAdditionalOptions="-DDLL_EXPORT -DFD_SETSIZE=1024"
 \t\t\t\tOptimization="2"
 \t\t\t\tEnableIntrinsicFunctions="true"
+\t\t\t\tAdditionalIncludeDirectories="$_pgmdir_/include"
 \t\t\t\tRuntimeLibrary="2"
 \t\t\t\tEnableFunctionLevelLinking="true"
 \t\t\t\tWarningLevel="3"
@@ -131,7 +133,7 @@
 \t\t\t/>
 \t\t\t<Tool
 \t\t\t\tName="VCLinkerTool"
-\t\t\t\tAdditionalDependencies="Ws2_32.lib Rpcrt4.lib"
+\t\t\t\tAdditionalDependencies="Ws2_32.lib Rpcrt4.lib $_pgmdir_/lib/libpgm.lib"
 \t\t\t\tOutputFile="../../../lib/libzmq.dll"
 \t\t\t\tGenerateDebugInformation="true"
 \t\t\t\tOptimizeReferences="2"
"""

patchstr_vcproj_zmq3 = r"""
--- builds\msvc\libzmq\libzmq.vcproj
+++ builds\msvc\libzmq\libzmq.vcproj
@@ -42,6 +42,7 @@
 				Name="VCCLCompilerTool"
 				AdditionalOptions="-DDLL_EXPORT -DFD_SETSIZE=1024 -D_CRT_SECURE_NO_WARNINGS"
 				Optimization="0"
+				AdditionalIncludeDirectories="$_pgmdir_/include"
 				PreprocessorDefinitions="NOMINMAX"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
@@ -60,8 +61,8 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Ws2_32.lib Rpcrt4.lib"
-				OutputFile="../../../lib/libzmq.dll"
+				AdditionalDependencies="Ws2_32.lib Rpcrt4.lib $_pgmdir_/lib/libpgm.lib"
+				OutputFile="../../../lib/libzmqd$_version_.dll"
 				GenerateDebugInformation="true"
 				TargetMachine="1"
 			/>
@@ -115,6 +116,7 @@
 				Name="VCCLCompilerTool"
 				AdditionalOptions="-DDLL_EXPORT -DFD_SETSIZE=1024 -D_CRT_SECURE_NO_WARNINGS"
 				Optimization="2"
+				AdditionalIncludeDirectories="$_pgmdir_/include"
 				EnableIntrinsicFunctions="true"
 				RuntimeLibrary="2"
 				EnableFunctionLevelLinking="true"
@@ -132,7 +134,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Ws2_32.lib Rpcrt4.lib"
-				OutputFile="../../../lib/libzmq.dll"
+				AdditionalDependencies="Ws2_32.lib Rpcrt4.lib $_pgmdir_/lib/libpgm.lib"
+				OutputFile="../../../lib/libzmq$_version_.dll"
 				GenerateDebugInformation="true"
 				OptimizeReferences="2"
@@ -288,10 +290,6 @@
 				>
 			</File>
 			<File
-				RelativePath="..\errno.cpp"
-				>
-			</File>
-			<File
 				RelativePath="..\..\..\src\fq.cpp"
 				>
 			</File>
@@ -550,10 +548,6 @@
 				>
 			</File>
 			<File
-				RelativePath="..\errno.hpp"
-				>
-			</File>
-			<File
 				RelativePath="..\..\..\src\fd.hpp"
 				>
 			</File>
"""

def zeromq3_extendcpp( builder ): #extractedpath, targetpath ):
  # ZeroMQ 3: Fetch C++ header from different URL and copy it to the include dir
  cppzmqSubDir = "cppzmq"
  cppzmqFullDir = os.path.join( builder.extractedpath, cppzmqSubDir )
  cppzmqHeaderSource = os.path.join( cppzmqFullDir, "zmq.hpp" )
  cppzmqHeaderTarget = builder.targetpath()+os.sep+"include"
  if os.path.exists( cppzmqFullDir ): # If FetchSourceSVN fetched the source before, the second call to fetch fails, so we first delete the download directory
    shutil.rmtree( cppzmqFullDir )
  FetchSourceSVN().fetch( URLCPPZMQ, cppzmqSubDir, builder.extractedpath )
  print "Patching zmq.hpp with \"#include <string>\""
  builder.replaceInFile( cppzmqHeaderSource, [ ("#include <cstring>", "#include <string>") ] )
  builder.copyfile( cppzmqHeaderSource, cppzmqHeaderTarget )

  '''
  # Doesn't work for https address if SSL is not compiled into the Python socket module
  if self.version.startswith("3"):
    cppzmqDir = "cppzmq"
    DownloadExtract().fetch( URLCPPZMQZIP, cppzmqDir, extractedpath )
  '''

class ZeroMQBuilder( VisualStudioBuilder ):
  """ subclass of VisualStudioBuilder with custom configure step """

  def patch(self):
    os.chdir( self.extractedpath )
    mapping = {'_pgmdir_': self.directories.enviro + '\\libpgm-5.1.118', "_version_" : self.version.replace(".", "") }
    if self.version.startswith("3"):
      tpl = string.Template(patchstr_vcproj_zmq3)
    else: # older version 2 patch
      tpl = string.Template(patchstr_vcproj_zmq2)

    #logformat = "%(levelname)8s %(message)s"
    #ext.patch.logger.setLevel(ext.patch.logging.INFO)
    #loghandler = ext.patch.logging.StreamHandler()
    #loghandler.setFormatter(ext.patch.logging.Formatter(logformat))
    #ext.patch.logger.addHandler(loghandler)

    p = ext.patch.fromstring(tpl.substitute(mapping))
    print "Patch 'change vcproj for openpgm' applied: ", p.apply()

    if self.machine.bits == 64:
      self.convertProjTo64("builds\msvc\c_local_lat\c_local_lat.vcproj")
      self.convertProjTo64("builds\msvc\c_local_thr\c_local_thr.vcproj")
      self.convertProjTo64("builds\msvc\c_remote_lat\c_remote_lat.vcproj")
      self.convertProjTo64("builds\msvc\c_remote_thr\c_remote_thr.vcproj")
      self.convertProjTo64("builds\msvc\libzmq\libzmq.vcproj")
      # Not available for version 2.1.3 and 2.1.7 and 2.2.0
      #self.convertProjTo64("builds\msvc\zmq_forwarder\zmq_forwarder.vcproj")
      #self.convertProjTo64("builds\msvc\zmq_queue\zmq_queue.vcproj")
      #self.convertProjTo64("builds\msvc\zmq_streamer\zmq_streamer.vcproj")      
      self.convertSolutionTo64("builds\\msvc\\msvc.sln")

    patchstr = """
--- builds\msvc\platform.hpp
+++ builds\msvc\platform.hpp
@@ -28,5 +28,5 @@
 //  Place any MSVC-specific definitions here.
 
 #define ZMQ_HAVE_WINDOWS
-
+#define ZMQ_HAVE_OPENPGM
 #endif

    """

    p = ext.patch.fromstring(patchstr)
    print "Patch '#define ZMQ_HAVE_OPENPGM' applied: ", p.apply()
    
    # Apply patch to prevent child processes from inheriting sockets. Created for ZeroMQ-2.2.0; might work for later versions.
    if self.version.startswith("2.2"):
      p = ext.patch.fromfile(sys.path[0] + "/library/additions/zeromq/zmq220-no-socket-inheritance.patch")
      print "Patch 'no socket inheritance' applied: ", p.apply()
      
    if self.version.startswith("3.2"):
      p = ext.patch.fromfile(sys.path[0] + "/library/additions/zeromq/zmq324-multicast-loopback-option.patch")
      print "Patch 'ZMQ v3 multicast loopback option' applied: ", p.apply()


  def make(self):  
    os.chdir( self.extractedpath+"" )

     
    #self.call_devenv("builds\\msvc\\msvc.sln", "")
    if self.debug:
      subprocess.call(["devenv.com", "builds\\msvc\\msvc.sln", "/Rebuild", "Debug", "/projectconfig", "Debug"] )
    else:
      subprocess.call(["devenv.com", "builds\\msvc\\msvc.sln", "/Rebuild", "Release", "/projectconfig", "Release"] )
    
  def install(self):
    os.chdir( self.extractedpath )    
    for dirname in "include", "lib", "bin":
      if os.path.exists(self.targetpath()+os.sep+dirname):
        shutil.rmtree(self.targetpath()+os.sep+dirname)
      self.copydir( dirname, self.targetpath()+os.sep+dirname )

    # ZeroMQ 3: Fetch C++ header from different URL and copy it to the include dir
    if self.version.startswith("3"):
      zeromq3_extendcpp( self )

    shortversion = self.version.replace(".", "")
    versionedlib = self.getlibdir() + os.sep + "libzmq" + shortversion + ".lib"
    if os.path.exists( versionedlib ):
      shutil.copyfile( versionedlib, self.getlibdir() + os.sep + "libzmq.lib")

    versionedlib = self.getlibdir() + os.sep + "libzmqd" + shortversion + ".lib"
    if os.path.exists( versionedlib ):
      shutil.copyfile( versionedlib, self.getlibdir() + os.sep + "libzmqd.lib")

    #todo move dll to bin dir

class ZeroMQConfigureMakeBuilder( ConfigureMakeBuilder ):
  
  def hasdebug(self):
    return True

  def targetpath(self):
    tp = ConfigureMakeBuilder.targetpath(self)
    if self.debug:
      return os.path.join( tp, "debug" )
    return tp

  def patch(self):
    os.chdir( self.extractedpath )
    # Apply patch to prevent child processes from inheriting sockets. Created for ZeroMQ-2.2.0; might work for later versions.
    if self.version.startswith("2.2"):
      p = ext.patch.fromfile(sys.path[0] + "/library/additions/zeromq/zmq220-no-socket-inheritance.patch")
      print "Patch 'no socket inheritance' applied: ", p.apply()
      
    if self.version.startswith("3.2"):
      p = ext.patch.fromfile(sys.path[0] + "/library/additions/zeromq/zmq324-multicast-loopback-option.patch")
      print "Patch 'ZMQ v3 multicast loopback option' applied: ", p.apply()


  def configureparameters(self):
    params = ["--with-pgm"]
    params.append('OpenPGM_CFLAGS="-I'+self.dependencies["openpgm"].installpath()+os.sep+'include"')
    params.append('OpenPGM_LIBS="-I'+self.dependencies["openpgm"].installpath()+os.sep+'lib'+' -lpgm"')

    if self.debug:
      params.append("--enable-debug")
      params.append("--program-suffix='d'")
    return params

  def configure(self):
    os.chdir( self.extractedpath )
    try:
      self.execute( "make clean" )
    except:
      pass
    ConfigureMakeBuilder.configure(self)

  def install(self):
    super(ZeroMQConfigureMakeBuilder, self).install()

    # ZeroMQ 3: extend with cpp header
    if self.version.startswith("3"):
      zeromq3_extendcpp( self );


def decider( machine, dirs ):
  return { "Linux" : ZeroMQConfigureMakeBuilder,
           "Windows" : ZeroMQBuilder }[machine.platform]

BUILDER = decider

